package com.xncoding.trans.config;

import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.xncoding.trans.cache.RedisCacheManagerResolver;
import com.xncoding.trans.entity.Menu;
import com.xncoding.trans.utils.ObjectUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.CacheManager;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.cache.interceptor.KeyGenerator;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.core.env.Environment;
import org.springframework.data.redis.cache.RedisCacheConfiguration;
import org.springframework.data.redis.cache.RedisCacheManager;
import org.springframework.data.redis.cache.RedisCacheWriter;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.RedisSerializationContext;

import java.lang.reflect.Method;
import java.time.Duration;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;

/**
 * RedisCacheConfig
 *
 * @author XiongNeng
 * @version 1.0
 * @since 2018/2/2
 */
@Configuration
@EnableCaching
public class RedisCacheConfig {
    private Logger logger = LoggerFactory.getLogger(this.getClass());

    @Autowired
    private Environment env;

    @Autowired
    private RedisConnectionFactory redisConnectionFactory;

//    @Bean
    public RedisCacheConfiguration cacheConfiguration(int times) {
        RedisCacheConfiguration cacheConfig = RedisCacheConfiguration.defaultCacheConfig()
                .entryTtl(Duration.ofSeconds(times))
                .disableCachingNullValues();
        return cacheConfig;
    }

    private Map<String, RedisCacheConfiguration> customCacheConfigurationMap() {
        Map<String, RedisCacheConfiguration> redisCacheConfigurationMap = new HashMap<>();
        redisCacheConfigurationMap.put("trl01", this.cacheConfiguration(300));
        redisCacheConfigurationMap.put("treeObjTestAll", this.cacheConfiguration(400));
        return redisCacheConfigurationMap;
    }

    @Bean
    @Primary
    public RedisCacheManager cacheManager() {

        RedisCacheManager rcm = RedisCacheManager.builder(RedisCacheWriter.nonLockingRedisCacheWriter(redisConnectionFactory))
                .cacheDefaults(this.cacheConfiguration(600))
                .withInitialCacheConfigurations(this.customCacheConfigurationMap())
                .transactionAware()
                .build();


        return rcm;
    }

    @Bean
    public CacheManager cacheClearManager(RedisConnectionFactory redisConnectionFactory) {

        RedisCacheManagerResolver cacheManager =
                new RedisCacheManagerResolver(RedisCacheWriter.nonLockingRedisCacheWriter(redisConnectionFactory),
                        this.getRedisCacheConfigurationWithTtl(900), // 默认策略，未配置的 value 会使用这个
                        this.customCacheConfigurationMap() // 指定 value策略
                );
        return cacheManager;
    }

        private RedisCacheConfiguration getRedisCacheConfigurationWithTtl(Integer seconds){
            Jackson2JsonRedisSerializer<Object> jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer<>(Object.class);
            ObjectMapper om = new ObjectMapper();
            om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
            om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
            jackson2JsonRedisSerializer.setObjectMapper(om);

            RedisCacheConfiguration redisCacheConfiguration = RedisCacheConfiguration.defaultCacheConfig();
            redisCacheConfiguration = redisCacheConfiguration.serializeValuesWith(
                    RedisSerializationContext
                            .SerializationPair
                            .fromSerializer(jackson2JsonRedisSerializer)
            ).entryTtl(Duration.ofSeconds(seconds));

            return redisCacheConfiguration;
        }


    /**
     * 自定义缓存key的生成类实现
     */
    @Bean(name = "myKeyGenerator")
    public KeyGenerator myKeyGenerator() {
        return new KeyGenerator() {
            @Override
            public Object generate(Object o, Method method, Object... params) {
                logger.info("自定义缓存，使用第一参数作为缓存key，params = " + Arrays.toString(params));
                if (params.length > 0) {
                    Menu param = (Menu) params[0];
                    String key = "";
                    if (ObjectUtil.isAllFieldNull(param)){
                        return 1111111;
                    }
                    return param.getId()+"="+param.toString();
                }

                // 仅仅用于测试，实际不可能这么写
                return params.toString();
            }
        };
    }

    /**
     * 自定义缓存key的生成类实现
     */
    @Bean(name = "mycleanKeyGenerator")
    public KeyGenerator mycleanKeyGenerator() {
        return new KeyGenerator() {
            @Override
            public Object generate(Object o, Method method, Object... params) {
                logger.info("清楚缓存key，params = " + Arrays.toString(params));
                if (params.length > 0) {
                    Menu param = (Menu) params[0];
                    String key = "";
                    if (ObjectUtil.isAllFieldNull(param)){
                        return 1111111;
                    }
                    return param.getId()+"=";
                }

                // 仅仅用于测试，实际不可能这么写
                return params.toString();
            }
        };
    }
}
